#ifndef ALGO_H
#define ALGO_H
#include <limits>

class Algo
{
public:
	static double pi() { return 3.1415926535897932384626433832795029L; }
	static double twopi() { return 2*3.1415926535897932384626433832795029L; }

	template<class T>
	static T min(T v1, T v2)
	{
		return v1 > v2 ? v2 : v1;
	}

	template<class T>
	static T max(T v1, T v2)
	{
		return v1 < v2 ? v2 : v1;
	}

	template<class T>
	static T clamp(T value,T minValue,T maxValue)
	{
		return min(maxValue,max(value,minValue));
	}

	template<class T>
	static T addClipOverflow(T a,T b)
	{
		if(b > 0)
		{
			if(std::numeric_limits<T>::max() - b < a)
				return std::numeric_limits<T>::max();
		}
		else
		{
			if(std::numeric_limits<T>::min() - b > a)
				return std::numeric_limits<T>::min();
		}

		return a + b;
	}

	template<class T>
	static T addClipOverflowInplace(T a,T b)
	{
		return a = addClipOverflow(a,b);
	}

	template<class T>
	static T seek(T var,T target,T rate,T dt)
	{
		if(var < target)
			return var = min(var + rate * dt,target);
		else
			return var = max(var - rate * dt,target);
	}

	template<class T,int N>
	static T pow(T base)
	{
		return(N < 0)
			? (1. / pow<-N>(base))
			: (((N & 1) ? base : 1)
				* ((N > 1) ? pow<N / 2>(base * base) : 1));
	}

	template<class T>
	static int sign(const T a)
	{
		if(a < -std::numeric_limits<T>::min())
			return -1;
		else if(std::numeric_limits<T>::min() < a)
			return 1;
		else
			return 0;
	}

	template<class T>
	static T rad2deg(const T& val)
	{
		return val * 180 / pi();
	}

	template<class T>
	static T deg2rad(const T& val)
	{
		return val * pi() / 180;
	}

	template<class T>
	static T normalizePeriodic(const T& min,const T& max,const T& value)
	{
		T range = max - min;
		if(range < std::numeric_limits<T>::min())
			return min;
		T normalized = value - range * floor((value - min) / range);
		// two security checks that can only happen due to roundoff
		if(normalized <= min)
			return min;
		if(max <= normalized)
			return min;
		return normalized;
	}

	// normalize the angle to be in a range between [-pi, pi[
	template<class T>
	static T normalizeAngle(const T& angle)
	{
		return normalizePeriodic(-pi(),pi(),angle);
	}

	// normalize the angle to be in a range between [0, 2pi[
	template<class T>
	static T normalizeAngle2(const T& angle)
	{
		return normalizePeriodic(0,twopi(),angle);
	}

	template<class T>
	static T round(const T& v)
	{
		return floor(v + T(0.5));
	}

	template<class T>
	static int roundToInt(const T& v)
	{
		return int(round(v));
	}

	// Linear interpolation between two arbitrary typed values
	template<class T>
	static T lerp(const T& val0,const T& val1,const T& t)
	{
		return val0*(1 - t) + val1 * t;
	}

	/// Returns true if v is a NaN value
	/// Use with care: allways code that you do not need to use that!
	template<class T>
	static bool isNaN(const T& v)
	{
		return std::isnan(v);
	}

	template<class T>
	static bool eq(const T& a,const T& b,const T& epsilon = std::numeric_limits<T>::epsilon())
	{
		return std::abs(a - b) < epsilon;
	}

	template<class T>
	static bool neq(const T& a,const T& b,const T& epsilon = std::numeric_limits<T>::epsilon())
	{
		return !eq(a,b,epsilon);
	}
};

#endif
